package com.example.demo.util;

import com.example.demo.annotation.ExcelProperty;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.WorkbookFactory;

import java.io.File;
import java.lang.reflect.Field;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 *	Excel 解析工具类（解析为 JavaBean 对象）
 * 
 */
public class ExcelReaderUtils {

	/**
	 * 将 Excel 解析为 JavaBean 对象
	 * @param file Excel 文件
	 * @param clazz 要转换的对象 class
	 * @return 返回对象的 List 集合
	 */
	public static <T> List<T> parse(File file, Class<T> clazz) {
		return parse(file, clazz, 0);
	}
	
	/**
	 *	将 Excel 解析为 JavaBean 对象
	 * @param file Excel 文件
	 * @param clazz 要转换的对象 class
	 * @param rowIndex       从第几行开始解析（包含标题），第一行为 0，依次类推
	 * @return 返回对象的 List 集合
	 */
	public static <T> List<T> parse(File file, Class<T> clazz, int rowIndex) {
		List<List<String>> resultList = parse(file, rowIndex);
		
		int currentRow = 1 + rowIndex;
		List<T> list = new ArrayList<>();
		for(List<String> rowDataList : resultList) {
			list.add(setField(clazz, rowDataList, currentRow));
			currentRow++;
		}
		return list;
	}

	/**
	 *	为对象的每一个属性赋值
	 * 
	 * @since 2019-05-19 10:20
	 * @param clazz 要转换的对象 class
	 * @param rowDataList 当前行的数据
	 * @return 返回转换后的对象实例
	 */
	private static <T> T setField(Class<T> clazz, List<String> rowDataList, int currentRow) {
		try {
			T obj = clazz.newInstance();
			Field[] fields = clazz.getDeclaredFields();
			for (Field field : fields) {
				ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
				Object value = getFieldValue(field, excelProperty, rowDataList, currentRow);
				if (value != null) {
					field.setAccessible(true);
					field.set(obj, value);
				}
			}
			return obj;
		} catch (Exception e) {
			throw new IllegalArgumentException(e);
		}
	}

	/**
	 *	将 Excel 解析为 List 集合
	 * @param file Excel 文件
	 * @param rowIndex 从第几行开始解析（包含标题），第一行为 0，依次类推
	 * @return 返回 Excel 数据集合
	 */
	private static List<List<String>> parse(File file, int rowIndex) {
		Sheet sheet = parseFile(file);
		int rowCount = sheet.getPhysicalNumberOfRows(); // 总行数
		int cellCount = sheet.getRow(rowIndex).getPhysicalNumberOfCells(); // 总列数，取标题所在行的列数

		List<List<String>> result = new ArrayList<>();
		for (int i = rowIndex + 1; i < rowCount; i++) {
			List<String> cellResult = new ArrayList<>();
			boolean beEmptyCell = true; // 判断当前行是否为空行，如果为空行，终止解析
			for (int j = 0; j < cellCount; j++) {
				String value = getCellValue(sheet, i, j);
				cellResult.add(value);
				if (value.length() > 0) {
					beEmptyCell = false;
				}
			}
			if (beEmptyCell) {
				break;
			}
			result.add(cellResult);
		}
		return result;
	}

    /**
     * 根据 Java 对象的属性的类型，将 Excel 中的值解析为相对应的类型
     * @param field 对象属性
     * @param excelProperty 对象属性的注解
     * @param dataList Excel 的当前行的数据
     * @param currentRow 当前行
     * @return 返回给定对象属性的值
     */
	@SuppressWarnings("rawtypes")
	private static Object getFieldValue(Field field, ExcelProperty excelProperty, List<String> dataList, int currentRow) {
		Object val;
        String value = dataList.get(excelProperty.index());
        int currentCell = excelProperty.index() +1;
        try {
            Class typeClass = field.getType();
			if(excelProperty.required() && isEmpty(value)) {
				throw new IllegalArgumentException(String.format("第【%s】行【%s】列，信息不能为空", currentRow, currentCell));
			}

			if (isEmpty(value)) {
				val = null;
			} else if (typeClass == Short.class) {
				val = Double.valueOf(value).shortValue();
			} else if (typeClass == Integer.class) {
				val = Double.valueOf(value).intValue();
			} else if (typeClass == Long.class) {
				val = Double.valueOf(value).longValue();
			} else if (typeClass == Float.class) {
				val = Double.valueOf(value).floatValue();
			} else if (typeClass == Double.class) {
				val = Double.valueOf(value);
			} else if (typeClass == Date.class || typeClass == LocalDate.class || typeClass == LocalDateTime.class) {
				val = parseDate(value, excelProperty.format(), typeClass, currentRow, currentCell);
			} else if (typeClass == Character.class) {
				val = value.charAt(0);
			} else if (typeClass == Boolean.class) {
				String[] bool = excelProperty.bool();
				val = value.equals(bool[0]) ? Boolean.TRUE : (value.equals(bool[1]) ? Boolean.FALSE : null);
				if (val == null) {
					throw new IllegalArgumentException(
							String.format("第【%s】行【%s】列，不能识别的值【%s】", currentRow, currentCell, value));
				}
			} else if(typeClass == String.class && excelProperty.decode().length > 0) {
				Map<String, String> map = convertToMap(excelProperty.decode());
				val = map.get(value);
				if(val == null) {
					throw new IllegalArgumentException(
					        String.format("第【%s】行【%s】列，不能识别的值【%s】", currentRow, currentCell, value));
				}
			} else {
			    val = value;
            }
		} catch (IllegalArgumentException e) {
			throw new IllegalArgumentException(e.getMessage());
		} catch (Exception e) {
			throw new IllegalArgumentException(
			        String.format("第【%s】行【%s】列，不能识别的值【%s】", currentRow, currentCell, value));
		}
		return val;
	}

	/**
	 * 解析导入模板
	 *
	 * @param file Excel 文件
	 * @return 返回 Excel 的第一个 Sheet
	 */
	public static Sheet parseFile(File file) {
		try {
			return WorkbookFactory.create(file).getSheetAt(0);
		} catch (Exception e) {
			throw new IllegalArgumentException("excel.parse.fail", e);
		}
	}

	/**
	 * 返回指定单元格的数据
	 *
	 * @since 2019-04-23 19:01
	 * @param sheet Sheet
	 * @param rowIndex  第几行，从 0 开始
	 * @param cellIndex 第几列，从 0 开始
	 * @return 返回指定单元格的值
	 */
	public static String getCellValue(Sheet sheet, int rowIndex, int cellIndex) {
		return getCellValue(sheet.getRow(rowIndex).getCell(cellIndex));
	}

	/**
	 * 格式化解析的数据
	 */
	private static String getCellValue(Cell cell) {
		String cellValue = "";
		if (cell == null) {
			return cellValue;
		}

		switch (cell.getCellType()) {
			case NUMERIC: // 数值类型
				if (DateUtil.isCellDateFormatted(cell)) {
					cellValue = cell.getDateCellValue().toString();
				} else {
					cellValue = String.valueOf(cell.getNumericCellValue());
				}
				break;
			case STRING: // 字符串类型
				cellValue = cell.getStringCellValue();
				break;
			case BOOLEAN: // 布尔类型
				cellValue = String.valueOf(cell.getBooleanCellValue());
				break;
			case FORMULA: // 公式类型
				cellValue = String.valueOf(cell.getCellFormula());
				break;
			case BLANK: // 空白类型
			case ERROR:
				cellValue = "";
				break;
			default:
				cellValue = cell.toString().trim();
				break;
		}
		return cellValue.trim();
	}
	
	private static Map<String, String> convertToMap(String[] args) {
		Map<String, String> map = new HashMap<>();
		for (int i = 0; i < args.length - 1; i+=2) {
			map.put(args[i], args[i + 1]);
		}
		return map;
	}
	
	private static boolean isEmpty(String str) {
		return str == null || str.length() == 0;
	}
	
	private static Object parseDate(String dateStr, String pattern, Class clazz, int currentRow, int currentCell) {
        try {
			if (clazz == Date.class) {
				return new SimpleDateFormat(pattern).parse(dateStr);
			} else if (clazz == LocalDate.class) {
				return LocalDate.parse(dateStr, DateTimeFormatter.ofPattern(pattern));
			} else if (clazz == LocalDateTime.class) {
				return LocalDateTime.parse(dateStr, DateTimeFormatter.ofPattern(pattern));
			}
			return null;
        } catch (ParseException e) {
            throw new IllegalArgumentException(
                    String.format("第【%s】行【%s】列，日期【%s】转换失败【%s】", currentRow, currentCell, dateStr, pattern));
        }
    }
}
